import { DIRECTION, PIECE_TYPE } from './SnakeConstants';
import { Piece } from './SnakePiece';
import { SnakeScene } from './SnakeScene';

const { ccclass, property } = cc._decorator;

@ccclass
export default class Board extends cc.Component {
    @property(cc.Float) private frameTime = 0.5;
    @property(cc.Float) private levelRatio = 0.05;
    @property(cc.Prefab) private piecePrefab: cc.Prefab = null;
    @property(cc.Node) private layout: cc.Node = null;

    public isStart = false;
    private pastTime = 0;
    private rowsNum: number = 16;
    private colsNum: number = 16;
    private gridWidth: number = 0;
    private pieceMap: Piece[][];
    private snake: Piece[];
    private moveDir = DIRECTION.RIGHT;
    public level = 0;

    private snakeScene: SnakeScene = null;

    onLoad() {
        this.gridWidth = this.layout.width / this.colsNum;
        this.pieceMap = [];
        for (let x = 0; x < this.colsNum; x++) {
            //初始化map的x轴数组
            this.pieceMap[x] = [];
            for (let y = 0; y < this.rowsNum; y++) {
                //从piecePrefab中实例化一个新的节点赋值给pieceNode
                let pieceNode = cc.instantiate(this.piecePrefab);
                //在layout节点中添加子节点pieceNode
                this.layout.addChild(pieceNode);
                //对pieceNode节点中的width/height/x/y等属性赋值
                pieceNode.width = this.gridWidth;
                pieceNode.height = this.gridWidth;
                pieceNode.x = x * this.gridWidth + pieceNode.width / 2;
                pieceNode.y = y * this.gridWidth + pieceNode.height / 2;
                //使得pieceMap[x][y]二维数组的元素是在pieceNode节点上获得的Piece组件上
                //
                this.pieceMap[x][y] = pieceNode.getComponent(Piece);
                this.pieceMap[x][y].x = x;
                this.pieceMap[x][y].y = y;
            }
        }

    }

    updateTick() {

        this.unschedule(this.tickHandler);
        let time = this.frameTime - (this.levelRatio * this.level);
        if (time < 0.1) {
            time = 0.1;
        }
        this.schedule(this.tickHandler, time);
    }

    tickHandler() {
        if (this.isStart) {
            this.moveSnake();
        }
    }

    init(snakeScene: SnakeScene) {
        this.snakeScene = snakeScene;
    }

    startGame() {
        this.reset();
        for (let i = 0; i < 10; i++) {
            this.addFood();
        }
        this.isStart = true;
        this.updateTick();
    }

    //初始化初始场景
    //首先使得整个地图均为背景使用clear()完成
    //创建snake[]数组
    //设置snake的头部/身体(两节)/尾部的初始化状态包括(图片属性/出发方向/进入方向)
    //将初始化的snake的元素放入snake[]数组
    //初始化初始移动方向为向右
    //初始化等级为0
    reset() {
        this.clear();
        this.snake = [];
        this.pieceMap[9][9].init(PIECE_TYPE.SNAKE_HEAD, DIRECTION.RIGHT, DIRECTION.RIGHT);
        this.pieceMap[8][9].init(PIECE_TYPE.SNAKE_BODY, DIRECTION.RIGHT, DIRECTION.RIGHT);
        this.pieceMap[7][9].init(PIECE_TYPE.SNAKE_BODY, DIRECTION.RIGHT, DIRECTION.UP);
        this.pieceMap[7][9].init(PIECE_TYPE.SNAKE_TAIL, DIRECTION.UP, DIRECTION.UP);
        this.snake.push(this.pieceMap[9][9]);
        this.snake.push(this.pieceMap[8][9]);
        this.snake.push(this.pieceMap[7][9]);
        this.snake.push(this.pieceMap[7][9]);
        this.moveDir = DIRECTION.RIGHT;
        this.level = 0;
    }

    //添加实物函数方法为
    //设置一个空数组blankList[]
    //遍历整个地图,当有元素图片属性为背景时,将这个元素加入blankList[]数组
    //对blankList[]数组随机取元素并将这个元素图片属性转换为食物
    addFood() {
        let blankList: Piece[] = [];
        for (let x = 0; x < this.colsNum; x++) {
            for (let y = 0; y < this.rowsNum; y++) {
                if (this.pieceMap[x][y].type === PIECE_TYPE.BLANK) {
                    blankList.push(this.pieceMap[x][y]);
                }
            }
        }
        //使得randomPiece为 blankList[]数组中的随机元素并将此元素的属性变为PIECE_TYPE.FOOD
        //(Math.random() * blankList.length) | 0  取随机数并向下取整
        let randomPiece = blankList[(Math.random() * blankList.length) | 0];
        if (Math.random() > 0.1) {
            randomPiece.type = PIECE_TYPE.FOOD;
        }
        else {
            randomPiece.type = PIECE_TYPE.supFood;
        }

        // for (let i = this.colsNum - 1; i >= 0; --i) {
        //     for (let j = 0; j < this.rowsNum; ++j) {
        //         if (this.pieceMap[i][j].type === PIECE_TYPE.BLANK) {
        //             this.pieceMap[i][j].type = PIECE_TYPE.FOOD;
        //             return ;

        //         }
        //     }
        // }
    }





    //初始化背景地图
    //使得地图中的每个元素都是背景属性
    clear() {
        for (let x = 0; x < this.colsNum; x++) {
            for (let y = 0; y < this.rowsNum; y++) {
                this.pieceMap[x][y].type = PIECE_TYPE.BLANK;
            }
        }
    }

    //使得等级能够实时更新为当前等级
    updateLevel(level: number) {
        if (level !== this.level) {
            this.level = level;
            this.updateTick();
        }
    }

    //判断当蛇变动的方向不为此时前行方向的相反方向时,使得蛇的移动方向改变为变动方向
    turnSnake(dir: DIRECTION) {
        if (this.snake[0].outDir !== -dir) {
            this.moveDir = dir;
        }
    }

    //定义snake的移动函数
    //首先初始化head的值为上述snake[]数组的首个元素(snake[0]),即为头部的状态
    //令head的进入方向的值为snake[1]元素的出发方向
    //令head的出发方向为移动方向
    //声明nextPiece组件作为snake的状态
    //判断蛇的方向
    //向上时,当头部移动到地图最顶端时使snake头部从地图本列的最底部出现,否则向上移动一格
    //其余方向规则与此相同
    //最后判断snake是否会撞到物体使用 this.moveSnakeToPiece(nextPiece)判断

    moveSnake() {
        let head = this.snake[0];
        let tail = this.snake[this.snake.length - 1];
        head.inDir = this.snake[1].outDir;
        head.outDir = this.moveDir;
        let nextPiece: Piece;
        let lastPiece: Piece;
        let last2Piece: Piece;
        switch (head.outDir) {
            case DIRECTION.UP:
                if (head.y === this.rowsNum - 1) {
                    // nextPiece = this.pieceMap[head.x][0];
                    this.isStart = false;
                    this.snakeScene.overGame();
                } else {
                    nextPiece = this.pieceMap[head.x][head.y + 1];

                }
                break;
            case DIRECTION.RIGHT:
                if (head.x === this.colsNum - 1) {
                    // nextPiece = this.pieceMap[0][head.y];
                    this.isStart = false;
                    this.snakeScene.overGame();
                } else {
                    nextPiece = this.pieceMap[head.x + 1][head.y];
                }
                break;
            case DIRECTION.DOWN:
                if (head.y === 0) {
                    // nextPiece = this.pieceMap[head.x][this.rowsNum - 1];
                    this.isStart = false;
                    this.snakeScene.overGame();
                } else {
                    nextPiece = this.pieceMap[head.x][head.y - 1];
                }
                break;
            case DIRECTION.LEFT:
                if (head.x === 0) {
                    // nextPiece = this.pieceMap[this.colsNum - 1][head.y];
                    this.isStart = false;
                    this.snakeScene.overGame();
                } else {
                    nextPiece = this.pieceMap[head.x - 1][head.y];
                }
                break;
        }
        switch (tail.outDir) {
            case DIRECTION.UP:
                if (tail.y === 1) {
                    lastPiece = this.pieceMap[tail.x][this.rowsNum-1];
                    last2Piece = this.pieceMap[tail.x][0];
                }
                else if(tail.y === 0){
                    lastPiece = this.pieceMap[tail.x][this.rowsNum-2];
                    last2Piece = this.pieceMap[tail.x][this.rowsNum-1];
                }
                else{
                lastPiece = this.pieceMap[tail.x][tail.y - 2];
                last2Piece = this.pieceMap[tail.x][tail.y - 1];
            }
                break;
            case DIRECTION.RIGHT:
            if(tail.x ===1){
                lastPiece = this.pieceMap[this.colsNum -1][tail.y];
                last2Piece = this.pieceMap[0][tail.y];
            }
            else if (tail.x ===0){
                lastPiece = this.pieceMap[this.colsNum -2][tail.y];
                last2Piece = this.pieceMap[this.colsNum -1][tail.y];
            }
            else{
                lastPiece = this.pieceMap[tail.x - 2][tail.y];
                last2Piece = this.pieceMap[tail.x - 1][tail.y];
            }
                break;
            case DIRECTION.DOWN:
            if(tail.y === this.rowsNum-2){
                lastPiece = this.pieceMap[tail.x][0];
                last2Piece = this.pieceMap[tail.x][this.rowsNum-1];
            }
            else if(tail.y === this.rowsNum-1){
                lastPiece = this.pieceMap[tail.x][1];
                last2Piece = this.pieceMap[tail.x][0];
            }
            else{
                lastPiece = this.pieceMap[tail.x][tail.y + 2];
                last2Piece = this.pieceMap[tail.x][tail.y + 1];
            }
                break;
            case DIRECTION.LEFT:
            if(tail.x === this.colsNum-2){
                lastPiece = this.pieceMap[0][tail.y];
                last2Piece = this.pieceMap[this.colsNum-1][tail.y ];
            }
            else if (tail.x === this.colsNum-1){
                lastPiece = this.pieceMap[1][tail.y];
                last2Piece = this.pieceMap[0][tail.y ];
            }
            else {
                lastPiece = this.pieceMap[tail.x + 2][tail.y];
                last2Piece = this.pieceMap[tail.x + 1][tail.y];
            }
                break;

        }
        this.moveSnakeToPiece(nextPiece, lastPiece, last2Piece);
    }

    //在piece组件下声明了一个nextpiece元素
    //判断移动后snake的状态
    //对nextPiece的type属性进行判断
    //当下一部图片属性为身体或者尾部时
    //游戏状态(isstart)判断为false,游戏场景转换为结束场景使用overGame()函数并跳出
    //当下一部图片属性为食物时
    //将下一部的属性初始化为(头部属性,出发方向为头部出发方向,进入方向为头部进入方向)
    //将此时作为头部的元素初始化为(身体属性,出发方向为头部出发方向,进入方向为头部进入方向)
    //使用unshift()方法向snake[]数组开头添加一个初始化为头部属性的nextPiece元素
    //然后使用onEatFood()转换snakesence
    //当下一部为空白背景时
    //声明一个新的空数组并初始化
    //使用forEach()对snake[]数组下的每个元素进行操作
    //操作为对图片类型进行判断,分别对头/身/尾进行操作
    //将操作完的nextPiece元素放入新的数组
    moveSnakeToPiece(nextPiece: Piece, lastPiece: Piece, last2Piece: Piece) {
        let head = this.snake[0];
        let tail = this.snake[this.snake.length - 1];
        switch (nextPiece.type) {
            case PIECE_TYPE.SNAKE_BODY:
            case PIECE_TYPE.SNAKE_TAIL:
                this.isStart = false;
                this.snakeScene.overGame();
                break;
            case PIECE_TYPE.FOOD:
                nextPiece.init(PIECE_TYPE.SNAKE_HEAD, head.outDir, head.inDir);
                head.init(PIECE_TYPE.SNAKE_BODY, head.outDir, head.inDir);
                this.snake.unshift(nextPiece);
                this.snakeScene.onEatFood();
                break;
            case PIECE_TYPE.supFood:
                last2Piece.init(PIECE_TYPE.SNAKE_BODY, tail.outDir, tail.inDir);
                lastPiece.init(PIECE_TYPE.SNAKE_TAIL, tail.outDir, tail.inDir);
                nextPiece.init(PIECE_TYPE.SNAKE_HEAD, head.outDir, head.inDir);
                head.init(PIECE_TYPE.SNAKE_BODY, head.outDir, head.inDir);
                tail.init(PIECE_TYPE.SNAKE_BODY, tail.outDir, tail.inDir);
                this.snake.unshift(nextPiece);
                this.snake.push(last2Piece, lastPiece);
                this.snakeScene.onEatSupFood();
                break;
            case PIECE_TYPE.BLANK:
                let newSnake = [];
                this.snake.forEach((piece, index) => {
                    switch (piece.type) {
                        case PIECE_TYPE.SNAKE_HEAD:
                            nextPiece.init(PIECE_TYPE.SNAKE_HEAD, piece.outDir, piece.inDir);
                            break;
                        case PIECE_TYPE.SNAKE_BODY:
                            nextPiece.init(PIECE_TYPE.SNAKE_BODY, nextPiece.outDir, piece.outDir);
                            break;
                        case PIECE_TYPE.SNAKE_TAIL:
                            nextPiece.init(PIECE_TYPE.SNAKE_TAIL, nextPiece.outDir, piece.outDir);
                            piece.type = PIECE_TYPE.BLANK;
                            break;
                    }
                    newSnake.push(nextPiece);
                    nextPiece = piece;//为保证赋值有效将赋值放在操作之后进行
                });
                // for (let piece of this.snake) {

                // }
                this.snake = newSnake;//将新建的newSnake数组赋值给原有的snake数组保证snake改变
        }
    }

    // update(dt: number) {
    //     if (this.isStart) {
    //         this.pastTime += dt;
    //         if (this.pastTime >= this.frameTime * (this.levelRatio**this.level)) {
    //             this.moveSnake();
    //             this.pastTime = 0;
    //         }
    //     }
    // }
}
